import hmac
import json
import requests
import urllib.parse as urlparse
import hashlib
import sys
import time

from coin.exchange.base.kr_rest.auth_base import AuthKey
from coin2.api_client.common import (SyncQueryer)
from enum import Enum

class Side(Enum):
  BUY = "Buy"
  SELL = "Sell"

class OrderType(Enum):
  MARKET = "Market"
  LIMIT = "Limit"
  STOP = "Stop"
  STOP_LIMIT = "StopLimit"
  MARKET_IF_TOUCHED = "MarketIfTouched"
  LIMIT_IF_TOUCHED = "LimitIfTouched"
  PEGGED = "Pegged"

class TimeInForce(Enum):
  GOOD_TILL_CANCEL = "GoodTillCancel"
  DAY = "Day"
  IMMEDIATE_OR_CANCEL = "ImmediateOrCancel"
  FILL_OR_KILL = "FillOrKill"

class BitmexAuth(requests.auth.AuthBase):
  def __init__(self, key):
    self._key = AuthKey.from_file(key)

  def _gen_signature(self, method: str, path_with_query: str, expires: str, data: str):
    # api-signature: A signature of the request you are making.
    # It is calculated as hex(HMAC_SHA256(apiSecret, verb + path + expires + data)).
    assert method, "method must be not None and not empty!"
    assert path_with_query, "path_with_query must be not None and not empty!"
    assert expires, "expires must be not None and not empty!"

    before_signature = ''.join(filter(None, [method, path_with_query, expires, data])).encode()
    mac = hmac.new(self._key.secret_key_bytes, before_signature, hashlib.sha256)
    return mac.hexdigest()

  def __call__(self, req: requests.PreparedRequest):
    # api-expires: A UNIX timestamp after which the request is no longer valid.
    # This is to prevent replay attacks.
    expires = str(int(time.time() + 60))
    parsed = urlparse.urlparse(req.url)
    path_with_query = parsed.path
    if parsed.query:
        path_with_query = path_with_query + '?' + parsed.query
    
    signature = self._gen_signature(req.method, path_with_query, expires, req.body)

    req.headers.update({'api-key': self._key.access_key})
    req.headers.update({'api-expires': expires})
    req.headers.update({'api-signature': signature})
    req.prepare_headers(req.headers)

    # print(req.headers)
    # print(req.body)
    # print(req.url)

    return req

class BitmexPrivateClient():
  def __init__(self, key_path: str, ip=None):
    self._auth = BitmexAuth(key_path)
    self._url = "https://www.bitmex.com"

    self._query = SyncQueryer(api_host=self._url, auth=self._auth, ip=ip)

  def get_order(self, symbol: str = None, filter: dict = None):
    method = "GET"
    params = {
      'symbol': symbol,
      'filter': filter and json.dumps(filter),
    }
    path = "/api/v1/order"

    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def cancel_order(self, order_id: str = None, cl_ord_id: str = None):
    one_and_only_one = (order_id == None and cl_ord_id != None) or (order_id != None and cl_ord_id == None)
    assert one_and_only_one, "order_id and cl_ord_id have one and only one!"

    method = "DELETE"
    data = {
      'orderID': order_id,
      'clOrdID': cl_ord_id,
    }
    path = "/api/v1/order"

    response = self._query.query(method=method, path=path, data=data)
    return response.json()

  def submit_order(
    self,
    symbol: str,
    side: Side,
    ord_type: OrderType,
    order_qty: str = None,
    price: str = None,
    cl_ord_id: str = None,
    time_in_force: TimeInForce = None,
  ):
    assert symbol, "symbol must be not None and not empty!"
    assert side, "side must be not None!"
    assert ord_type, "ord_type must be not None!"

    method = "POST"
    data = {
      'symbol': symbol,
      'side': side.value,
      'ordType': ord_type.value,
      'orderQty': order_qty,
      'price': price,
      'clOrdID': cl_ord_id,
      'timeInForce': time_in_force and time_in_force.value,
    }
    path = "/api/v1/order"

    response = self._query.query(method=method, path=path, data=data)
    return response.json()

# api doc url
# Authenticating https://www.bitmex.com/app/apiKeysUsage
# Api            https://www.bitmex.com/api/explorer/

if __name__ == "__main__":
  key_path = sys.argv[1]
  
  bitmex = BitmexPrivateClient(key_path)
  info = bitmex.get_order("XBTUSD", filter={"open": True})
  # info = bitmex.cancel_order(cl_ord_id="order-1520076301523718000")
  # info = bitmex.submit_order("XBTUSD", Side.SELL, OrderType.LIMIT, "0.0000001", "100000", time_in_force=TimeInForce.GOOD_TILL_CANCEL)
  print(json.dumps(info, indent=2))
